package serveur;

import java.rmi.RemoteException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Cette classe représente l'application qui sera téléchargée par le client.
 * 
 * @author Loic Faizant & Romain Roufast
 * 
 */
@SuppressWarnings("serial")
public class TwitterDistantImpl extends UnicastRemoteObject implements
		TwitterDistant {

	/**
	 * Un dictionnaire comprenant la liste des clients et leur état (Connecté ou
	 * Non-Connecté).
	 */
	private HashMap<ClientDistant, EtatConnexion> tweetos;
	
	/**
	 * Les clients inscrits
	 */
	private List<Client> listeClientsInscrits;

	/**
	 * Contient les clients et leurs Tweets associés.
	 */
	private HashMap<Client, List<Tweet>> tweets;

	/**
	 * Contient les followers d'un client.
	 */
	private HashMap<Client, List<Client>> followers;

	/**
	 * Contient les Tweets ayant été relayés par un utilisateur.
	 */
	private HashMap<Client, List<Tweet>> tweetRelayes;

	/**
	 * Service utilisable uniquement par les clients connectés.
	 */
	private ServiceClientConnecteRemote serviceConnecte;

	/**
	 * Constructeurs
	 * 
	 * @throws RemoteException
	 */
	public TwitterDistantImpl() throws RemoteException {
	}

	public TwitterDistantImpl(int port) throws RemoteException {
		super(port);
		this.tweetos = new HashMap<ClientDistant, EtatConnexion>();
		this.followers = new HashMap<Client, List<Client>>();
		this.tweets = new HashMap<Client, List<Tweet>>();
		this.tweetRelayes = new HashMap<Client, List<Tweet>>();
		this.listeClientsInscrits = new ArrayList<Client>();
	}

	public TwitterDistantImpl(ServiceClientConnecteRemote serviceConnecte,
			int port) throws RemoteException {
		super(port);
		this.serviceConnecte = serviceConnecte;
		this.tweetos = new HashMap<ClientDistant, EtatConnexion>();
		this.followers = new HashMap<Client, List<Client>>();
		this.tweets = new HashMap<Client, List<Tweet>>();
		this.tweetRelayes = new HashMap<Client, List<Tweet>>();
		this.listeClientsInscrits = new ArrayList<Client>();
	}

	public TwitterDistantImpl(int port, RMIClientSocketFactory csf,
			RMIServerSocketFactory ssf) throws RemoteException {
		super(port, csf, ssf);
	}

	@Override
	synchronized public ServiceClientConnecteRemote seConnecter(ClientDistant cd, String pseudo, String mdp) throws RemoteException {
		for (Client cc : listeClientsInscrits) {
			if(cc.getPseudo().equals(pseudo) && cc.getMdp().equals(mdp)) {
				cd.setMdp(mdp);
				cd.setPrenom(cc.getPrenom());
				cd.setNom(cc.getNom());
				cd.setPseudo(pseudo);
				
				tweetos.put(cd, EtatConnexion.CONNECTE);				
				return serviceConnecte;
			}
		}		
		System.out.println("[CONNEXION] Echec");
		return null;
	}

	@Override
	synchronized public ServiceClientConnecteRemote sInscrire(ClientDistant client, String nom,
			String prenom, String mdp, String pseudo) throws RemoteException {

		for (Client c : listeClientsInscrits) {
			if (c.getPseudo().equals(pseudo))
				return null;
		}

		Client cc = new Client(nom, prenom, mdp, pseudo);
		listeClientsInscrits.add(cc);
		
		client.setMdp(mdp);
		client.setPrenom(prenom);
		client.setNom(nom);
		client.setPseudo(pseudo);
		
		tweetos.put(client, EtatConnexion.CONNECTE);		
		return serviceConnecte;
	}

	@Override
	public Set<String> getUtilisateurs() throws RemoteException {
		Set<String> usersSet = new HashSet<String>();
		for (Client cc : listeClientsInscrits) {
			usersSet.add(cc.getPseudo());
		}

		return usersSet;
	}

	@Override
	public Set<String> getTopics() throws RemoteException {
		Set<String> topicSet = new HashSet<String>();
		if(tweets.values().size() > 0) {
			List<Tweet> l = (List<Tweet>) tweets.values().toArray()[0];
			for (Tweet t : l) {
				String topic = "#" + t.getTopic();
				if(!topicSet.contains((String) topic))
					topicSet.add("#" + t.getTopic());
			}
		}
		return topicSet;
	}

	@Override
	public List<Tweet> getTweetsParUtilisateur(String pseudo)
			throws RemoteException {
		for (Entry<Client, List<Tweet>> e : tweets.entrySet()) {
			Client c = e.getKey();
			if (c.getPseudo().equals(pseudo))
				return e.getValue();
		}
		return null;
	}

	@Override
	public List<Tweet> getTweetsParTopic(String topic) throws RemoteException {
		List<Tweet> tweetsList = new ArrayList<Tweet>();
		for (List<Tweet> ltweet : tweets.values()) {
			tweetsList.addAll(ltweet);
		}
		
		return tweetsList;
	}

	@Override
	public void tweeter(String topic, String message, ClientDistant client)
			throws RemoteException {
		boolean tw = false;
		Client tmp = null;
		
		for (Client cc : listeClientsInscrits) {
			if(cc.getPseudo().equals(client.getPseudo()))
				tmp = cc;
		}
		for (Entry<Client, List<Tweet>> e : tweets.entrySet()) {
			Client c = e.getKey();
			if(c.getPseudo().equals(tmp.getPseudo())) {
				e.getValue().add(new Tweet(topic, message, c.getPseudo()));
				tw = true;
			}
		}
		if(tw==false) {
			ArrayList<Tweet> a = new ArrayList<Tweet>();
			Tweet t = new Tweet(topic, message, tmp.getPseudo());
			a.add(t);
			tweets.put(tmp, a);
		}
	}
	
	
	@Override
	public void tweeter(Tweet t, ClientDistant client) throws RemoteException {
		boolean tw = false;
		Client tmp = null;
		
		for (Client cc : listeClientsInscrits) {
			if(cc.getPseudo().equals(client.getPseudo()))
				tmp = cc;
		}
		for (Entry<Client, List<Tweet>> e : tweets.entrySet()) {
			Client c = e.getKey();
			if(c.getPseudo().equals(tmp.getPseudo())) {
				e.getValue().add(t);
				tw = true;
			}
		}
		if(tw==false) {
			ArrayList<Tweet> a = new ArrayList<Tweet>();
			a.add(t);
			tweets.put(tmp, a);
		}
	}
	

	@Override
	public void follow(String pseudo, ClientDistant client)
			throws RemoteException {
		boolean exists = false;
		
		String pseudoClient = client.getPseudo();
		Client cc = null;
		Client clientFollowed = null;
		
		if(pseudo.equals(pseudoClient)) return;
		
		// Récupérer le client
		for (Client tmp : listeClientsInscrits) {
			if(tmp.getPseudo().equals(pseudoClient))
				cc = tmp;
			else if(tmp.getPseudo().equals(pseudo))
				clientFollowed = tmp;
		}
		
		// Si le client follow deja qqun
		for (Entry<Client, List<Client>> e : followers.entrySet()) {
			Client c = e.getKey();
			if(c.getPseudo().equals(pseudoClient)) {
				e.getValue().add(clientFollowed);
				exists = true;
			}
		}
		
		// Sinon...
		if(!exists) {
			List<Client> a = new ArrayList<Client>();
			a.add(clientFollowed);
			followers.put(cc, a);
		}
		
	}

	@Override
	synchronized public void seDeconnecter(ClientDistant c) throws RemoteException {
		for (Entry<ClientDistant, EtatConnexion> e : tweetos.entrySet()) {
			ClientDistant client = e.getKey();
			if(client.getPseudo().equals(c.getPseudo())) {
				tweetos.remove(client);
			}
		}
	}

	public void setServiceClientConnecte(
			ServiceClientConnecteRemote serviceClient) {
		this.serviceConnecte = serviceClient;
	}

	@Override
	public List<Tweet> afficherTweetUtilisateursFollowed(ClientDistant c)
			throws RemoteException {
		List<Tweet> l = new ArrayList<Tweet>();
		
		for (Client client : listeClientsInscrits) {
			if(client.getPseudo().equals(c.getPseudo())) {
				List<Client> lc = followers.get(client);
				if(lc != null) {
					for (Client cc : lc) {
						List<Tweet> tmp = tweets.get(cc);
						if(tmp != null)
							l.addAll(tmp);
					}
				}
			}
		}
		
		return l;
	}

	

}
